#include <string.h>

#include "lib.h"

#include "tbllim.h"

#include "crapserv.h"
#include "crapconn.h"
#include "crapgame.h"
#include "crapssql.h"
#include "crapsave.h"
#include "stdncerr.h"
#include "opts.h"
#include "log.h"
#include "badpacketmsgs.h"
#include "crproto.h"
#include "XMLTranslator.h"

#define CRAPS_APP1		(uint8)'C'
#define CRAPS_APP2		(uint8)'R'

// kharmon.  06-16-06.  #3077.  SicBo Implementation.
#define SICBO_APP1		(uint8)'S'
#define SICBO_APP2		(uint8)'B'


static TrackSQL* tracker = (TrackSQL *) 0;

void
CrapsServer::startTracker()
{
	ServerOpts::load();

	tracker = new TrackSQL(ServerOpts::dbdsn, ServerOpts::dblogin, ServerOpts::dbpasswd);

	// CSN-8891
	webServiceCall = new WebServiceCall();
}

void
CrapsServer::stopTracker()
{
	delete tracker;
	tracker = 0;

	delete webServiceCall;
	webServiceCall = 0;
}

bool8
CrapsServer::initSuccess()
{
	if (tracker && tracker->initOK())
		return Server::initSuccess();
	else
	{
		Log::msgError("Failed to connect to the database.  Aborting.");
		return false;
	}
}

bool8
CrapsConnection::parseAnsiCasinoLogin(int32* Err)
{
	Info* info;
	int32 i=0;

	ASSERT( msgbuf[i] == 'L' ); i++;
 
    uint8 ucApp1 = msgbuf[i++];
    uint8 ucApp2 = msgbuf[i++];

    // kharmon.  06-16-06.  #3077.  SicBo Implementation.  Figure out which game we're talking about.
    m_eGameId = INVALID_ID;
    
	if (ucApp1 == CRAPS_APP1 && ucApp2 == CRAPS_APP2)
    {
        m_eGameId = CRAPS_GAME_ID;
    }
    else if(ucApp1 == SICBO_APP1 && ucApp2 == SICBO_APP2)
    {
        m_eGameId = SICBO_GAME_ID;
    }

	if (m_eGameId == INVALID_ID || msgbuf[i++] != PROTO_VERSION1 || msgbuf[i++] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c.%c%c",
				msgbuf[1],msgbuf[2],msgbuf[3],msgbuf[4]));
		return false;
	}

	info = new Info();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	char login[PIDLEN+1];

	memcpy(login, msgbuf + i, PIDLEN);
	login[PIDLEN] = '\0';
	strlwr(login);
	i += PIDLEN;

	info->login = login;

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{	
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-7075: New Authentication
		// CSN-8909: Look for clientType in Integration API
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}
	
	if (tracker)
	{
		//int32 result;
		bool8 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
		
	}
	info->pid = info->login;

	memset(info->gid, 0xff, sizeof(CrapsGameID));

	casinoSpecific = info;

    // kharmon.  06-16-06.  #3077.  SicBo Implementation.  We'll need this to talk to the database.
    m_pDBInterface = GameDBInterface::CreateInterface(this, tracker);

	checkForcedRolls(info->login);

	return true;
}


bool8
CrapsConnection::parseUnicodeCasinoLogin(int32* Err)
{
	Info* info;
	int32 i=0;

    // First byte should be M for multilingual/unicode
    ASSERT( msgbuf[i] == 'M' ); i++;
 
    // Second byte should be L for login
	ASSERT( msgbuf[i] == 'L' ); i++;
    uint8 ucApp1 = msgbuf[i++];
    uint8 ucApp2 = msgbuf[i++];

    // kharmon.  06-16-06.  #3077.  SicBo Implementation.  Figure out which game we're talking to.
    m_eGameId = INVALID_ID;    

	if (ucApp1 == CRAPS_APP1 && ucApp2 == CRAPS_APP2)
    {
        m_eGameId = CRAPS_GAME_ID;
    }
    else if(ucApp1 == SICBO_APP1 && ucApp2 == SICBO_APP2)
    {
        m_eGameId = SICBO_GAME_ID;
    }

	if (m_eGameId == INVALID_ID || msgbuf[i++] != PROTO_VERSION1 || msgbuf[i++] != PROTO_VERSION2)
	{
		*Err = LOGINERR_BADVERSION;
		DEBUGMSG(("Login: incorrect version: %c%c.%c%c",
				msgbuf[2],msgbuf[3],msgbuf[4],msgbuf[5]));
		return false;
	}

	info = new Info();
	if (!info)
	{
		*Err = LOGINERR_TRYLATER;
		return false;
	}

	i += info->login.deserialize(msgbuf + i);

	info->forMoney = (bool8)(msgbuf[i++] != 0);

	// CSN-10426: with this modification, we don't need iClientID sent by the client, so we skip it
	String sClientTypeIntAPI = String::emptyString;
	i++;	// Skip ClientID

	if (xmlTranslator && xmlTranslator->UsingFunPID())
	{
		// CSN-7076: New Authentication (fun play)
		bool8 result;
		tracker->lock();
		result = tracker->getAvailableFunID( info->login );
		tracker->unlock();
		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			info = 0;
			return false;
		}
	}
	else
	{	
		// CSN-7075: New Authentication
		String sToken = String::emptyString;
		i =+ sToken.deserialize(&msgbuf[i]);
		
		// CSN-8909: Look for clientType in Integration API		
		if (!wsCall()->Authenticate(sToken, info->login, sClientTypeIntAPI, m_sCurrency))
		{
			bool8 bAuth = false;
			
			// CSN-12167: connect with no token? In that case, white-listed PID is sent in <Token> field
			#ifdef DEBUGSPIN
			if (ServerOpts::bAllowTesting)
			{
				bAuth = tracker->isFreeFromAuthentication(sToken);					
				if (bAuth)
					info->login = sToken;
			}
			#endif

			if (!bAuth)
			{
				*Err = LOGINERR_CONTACTCUSTSERVICE;
				delete info;
				info = 0;
				return false;
			}
		}
	}
	
	if (tracker)
	{
		//int32 result;
		bool8 result;

		tracker->lock();
		//result = tracker->validateLogin(info->login, info->sessionId, info->pid);
		result = tracker->createRTGSession(info->forMoney, info->login, info->sessionId, sClientTypeIntAPI);
		tracker->unlock();

		if (!result)
		{
			*Err = LOGINERR_CONTACTCUSTSERVICE;
			delete info;
			return false;
		}
		
	}
	info->pid = info->login;

	memset(info->gid, 0xff, sizeof(CrapsGameID));

	casinoSpecific = info;

    // kharmon.  06-16-06.  #3077.  SicBo Implementation.  We'll need this to talk to the database.
    m_pDBInterface = GameDBInterface::CreateInterface(this, tracker);

	checkForcedRolls(info->login);

	return true;
}


void
CrapsConnection::removeCasinoSpecific()
{
	delete (Info *) casinoSpecific;

	casinoSpecific = NULL;
}

int32
CrapsConnection::encode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}

int32
CrapsConnection::decode(uint8* In, uint8* Out, int32 Len)
{
	memcpy(Out, In, Len);
	return Len;
}



void
CrapsConnection::trackaddBadPacket(int32 error_reason, char MessageType, char *msgbuf, int32 msglen)
{
	Info *info = casinoSpecific;

	if (tracker && info)
	{
		tracker->lock();
        tracker->addBadPacket((int32) m_eGameId, 0, sBadPacketMessages[error_reason].message, sBadPacketMessages[error_reason].severity, info->pid, MessageType, (uint8*)msgbuf, msglen);
		tracker->unlock();
	}
}

int32
CrapsConnection::trackValidateLogin()
{
    Info * info = casinoSpecific;
    int32 result = TrackSQL::ExecFailed;

    if (tracker && info)
    {
        tracker->lock();
		result = tracker->validateLogin(info->login, info->sessionId, info->pid);
        tracker->unlock();
    }

    return result;
}

int32
CrapsConnection::trackValidateSession()
{
    Info * info = casinoSpecific;
    int32 result = TrackSQL::ExecFailed;

    if (tracker && info)
    {
        tracker->lock();
		result = tracker->validateSession(info->pid, info->sessionId);
        tracker->unlock();
    }

    return result;
}

void CrapsConnection::checkForcedRolls(String login)
{
	if(ServerOpts::bAllowTesting && ServerOpts::IsDebugCasinoName())
	{
		m_bForcedRolls = false;
		String sForcedDices = ((CrapsSQL *) getGameDBInterface())->getForcedRolls(login, m_eGameId, 0);
		m_iForcedRollCount = sForcedDices.getLength();
		if (m_iForcedRollCount > 0)
		{
			m_iForcedRollValues = new uint8[m_iForcedRollCount];
			for(int i=0; i<m_iForcedRollCount; i++)
			{
				m_iForcedRollValues[i] = (uint8) sForcedDices.substring(i,1).toUInt16();
			}

			m_bForcedRolls = true;
			m_iForcedRollRound = 0;
		}
	}
}

uint8 CrapsConnection::getForcedRoll()
{
	int32 iIndex = (m_iForcedRollRound++) % m_iForcedRollCount;
	return m_iForcedRollValues[iIndex];
}

void CrapsConnection::trackLogoutSession()
{
    Info * info = casinoSpecific;

	if (tracker && info && !info->login.isEmpty())
    {
        tracker->lock();
		tracker->logout(ServerOpts::casinoId, info->login, info->forMoney, String::emptyString, info->sessionId);
        tracker->unlock();
    }
}